#include <jni.h>
#include <string.h>
#include <malloc.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <android/log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <dlfcn.h>
#include <sys/wait.h>
#include <linux/inotify.h>
#include <errno.h>

#define  LOG_TAG    "native-dev"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)


const char *APP_SIGNATURE = "7A15A70CF6E75397DE3A628909ABB0D2";

void ByteToHexStr(const char *source, char *dest, int sourceLen) {
    short i;
    char highByte, lowByte;

    for (i = 0; i < sourceLen; i++) {
        highByte = source[i] >> 4;
        lowByte = source[i] & 0x0f;
        highByte += 0x30;

        if (highByte > 0x39) {
            dest[i * 2] = highByte + 0x07;
        } else {
            dest[i * 2] = highByte;
        }

        lowByte += 0x30;
        if (lowByte > 0x39) {
            dest[i * 2 + 1] = lowByte + 0x07;
        } else {
            dest[i * 2 + 1] = lowByte;
        }
    }
}

// byte数组转MD5字符串
jstring ToMd5(JNIEnv *env, jbyteArray source) {
    // MessageDigest类
    jclass classMessageDigest = env->FindClass("java/security/MessageDigest");
    // MessageDigest.getInstance()静态方法
    jmethodID midGetInstance = env->GetStaticMethodID(classMessageDigest, "getInstance",
                                                      "(Ljava/lang/String;)Ljava/security/MessageDigest;");
    // MessageDigest object
    jobject objMessageDigest = env->CallStaticObjectMethod(classMessageDigest, midGetInstance,
                                                           env->NewStringUTF("md5"));

    // update方法，这个函数的返回值是void，写V
    jmethodID midUpdate = env->GetMethodID(classMessageDigest, "update", "([B)V");
    env->CallVoidMethod(objMessageDigest, midUpdate, source);

    // digest方法
    jmethodID midDigest = env->GetMethodID(classMessageDigest, "digest", "()[B");
    jbyteArray objArraySign = (jbyteArray) env->CallObjectMethod(objMessageDigest, midDigest);

    jsize intArrayLength = env->GetArrayLength(objArraySign);
    jbyte *byte_array_elements = env->GetByteArrayElements(objArraySign, NULL);
    size_t length = (size_t) intArrayLength * 2 + 1;
    char *char_result = (char *) malloc(length);
    memset(char_result, 0, length);

    // 将byte数组转换成16进制字符串，发现这里不用强转，jbyte和unsigned char应该字节数是一样的
    ByteToHexStr((const char *) byte_array_elements, char_result, intArrayLength);
    // 在末尾补\0
    *(char_result + intArrayLength * 2) = '\0';

    jstring stringResult = env->NewStringUTF(char_result);
    // release
    env->ReleaseByteArrayElements(objArraySign, byte_array_elements, JNI_ABORT);
    // 释放指针使用free
    free(char_result);
    env->DeleteLocalRef(classMessageDigest);
    env->DeleteLocalRef(objMessageDigest);

    return stringResult;
}

//获取应用签名
jstring loadSignature(JNIEnv *env, jobject context) {
    // 获得Context类
    jclass cls = env->GetObjectClass(context);
    // 得到getPackageManager方法的ID
    jmethodID mid = env->GetMethodID(cls, "getPackageManager",
                                     "()Landroid/content/pm/PackageManager;");

    // 获得应用包的管理器
    jobject pm = env->CallObjectMethod(context, mid);

    // 得到getPackageName方法的ID
    mid = env->GetMethodID(cls, "getPackageName", "()Ljava/lang/String;");
    // 获得当前应用包名
    jstring packageName = (jstring) env->CallObjectMethod(context, mid);

    // 获得PackageManager类
    cls = env->GetObjectClass(pm);
    // 得到getPackageInfo方法的ID
    mid = env->GetMethodID(cls, "getPackageInfo",
                           "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    // 获得应用包的信息
    jobject packageInfo = env->CallObjectMethod(pm, mid, packageName, 0x40); //GET_SIGNATURES = 64;
    // 获得PackageInfo 类
    cls = env->GetObjectClass(packageInfo);
    // 获得签名数组属性的ID
    jfieldID fid = env->GetFieldID(cls, "signatures", "[Landroid/content/pm/Signature;");
    // 得到签名数组
    jobjectArray signatures = (jobjectArray) env->GetObjectField(packageInfo, fid);
    // 得到签名
    jobject signature = env->GetObjectArrayElement(signatures, 0);

    // 获得Signature类
    cls = env->GetObjectClass(signature);
    // 得到toCharsString方法的ID
    mid = env->GetMethodID(cls, "toByteArray", "()[B");
    // 返回当前应用签名信息
    jbyteArray signatureByteArray = (jbyteArray) env->CallObjectMethod(signature, mid);

    return ToMd5(env, signatureByteArray);
}

//检测签名是否匹配
jboolean checkSignature(
        JNIEnv *env, jobject context) {

    jstring appSignature = loadSignature(env,
                                         context); // 当前 App 的签名
    jstring releaseSignature = env->NewStringUTF(APP_SIGNATURE); // 发布时候的签名
    const char *charAppSignature = env->GetStringUTFChars(appSignature, NULL);
    const char *charReleaseSignature = env->GetStringUTFChars(releaseSignature, NULL);

//    LOGI("  start cmp  getSignature");
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, charAppSignature);
//    LOGI("  start cmp  getReleaseSignature");

    jboolean result = JNI_FALSE;
    // 比较是否相等
    if (charAppSignature != NULL && charReleaseSignature != NULL) {
        if (strcmp(charAppSignature, charReleaseSignature) == 0) {
            result = JNI_TRUE;
        }
    }

    env->ReleaseStringUTFChars(appSignature, charAppSignature);
    env->ReleaseStringUTFChars(releaseSignature, charReleaseSignature);

    return result;
}

static jobject getApplication(JNIEnv *env) {
    jobject application = NULL;
    jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");
    if (activity_thread_clz != NULL) {
        jmethodID currentApplication = env->GetStaticMethodID(
                activity_thread_clz, "currentApplication", "()Landroid/app/Application;");
        if (currentApplication != NULL) {
            application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication);
        } else {
            //           LOGE("Cannot find method: currentApplication() in ActivityThread.");
        }
        env->DeleteLocalRef(activity_thread_clz);
    } else {
//        LOGE("Cannot find class: android.app.ActivityThread");
    }
    return application;
}

/**
 * 检查加载该so的应用的签名，与预置的签名是否一致
 */
static jboolean checkSignature(JNIEnv *env) {
    // 调用 getContext 方法得到 Context 对象
    jobject appContext = getApplication(env);

    if (appContext != NULL) {
        jboolean signatureValid = checkSignature(
                env, appContext);
        return signatureValid;
    }
    return JNI_FALSE;
}

//获取TracePid
int get_number_for_str(char *str) {
    if (str == NULL) {
        return -1;
    }
    char result[20];
    int count = 0;
    while (*str != '\0') {
        if (*str >= 48 && *str <= 57) {
            result[count] = *str;
            count++;
        }
        str++;
    }
    int val = atoi(result);
    return val;
}

//开启循环轮训检查TracePid字段
void *thread_function(void *argv) {
    int pid = getpid();
    char file_name[20] = {'\0'};
    sprintf(file_name, "/proc/%d/status", pid);
    char linestr[256];
    int i = 0, traceid;
    FILE *fp;
    while (1) {
        i = 0;
        fp = fopen(file_name, "r");
        if (fp == NULL) {
            break;
        }
        while (!feof(fp)) {
            fgets(linestr, 256, fp);
            if (i == 5) {
                traceid = get_number_for_str(linestr);
                LOGD("traceId:%d", traceid);
                if (traceid > 0) {
                    LOGD("I was be traced...trace pid:%d", traceid);
                    //华为P9会主动给app附加一个进程
                    exit(0);
                }
                break;
            }
            i++;
        }
        fclose(fp);
        sleep(5);
    }
    return ((void *) 0);
}

void create_thread_check_traceid() {
    pthread_t t_id;
    int err = pthread_create(&t_id, NULL, thread_function, NULL);
    if (err != 0) {
        LOGD("create thread fail: %s\n", strerror(err));
    }
}

//调试端口检测
void CheckPort23946ByTcp() {
    FILE *pfile = NULL;
    char buf[0x1000] = {0};
    // 执行命令
    char *strCatTcp = "cat /proc/net/tcp |grep :5D8A";
    //char* strNetstat="netstat |grep :23946";
    pfile = popen(strCatTcp, "r");
    if (NULL == pfile) {
        LOGD("CheckPort23946ByTcp popen打开命令失败!\n");
        return;
    }
    // 获取结果
    while (fgets(buf, sizeof(buf), pfile)) {
        // 执行到这里，判定为调试状态
        LOGD("执行cat /proc/net/tcp |grep :5D8A的结果:\n");
        LOGD("%s", buf);
    }//while
    pclose(pfile);
}

//调试进程名检测
void SearchObjProcess() {
    FILE *pfile = NULL;
    char buf[0x1000] = {0};
    // 执行命令
    //pfile=popen("ps | awk '{print $9}'","r"); // 部分不支持awk命令
    pfile = popen("ps", "r");
    if (NULL == pfile) {
        LOGD("SearchObjProcess popen打开命令失败!\n");
        return;
    }
    // 获取结果
    LOGD("popen方案:\n");
    while (fgets(buf, sizeof(buf), pfile)) {
        // 打印进程
        LOGD("遍历进程:%s\n", buf);
        // 查找子串
        char *strA = NULL;
        char *strB = NULL;
        char *strC = NULL;
        char *strD = NULL;
        strA = strstr(buf, "android_server");
        strB = strstr(buf, "gdbserver");
        strC = strstr(buf, "gdb");
        strD = strstr(buf, "fuwu");
        if (strA || strB || strC || strD) {
            // 执行到这里，判定为调试状态
            LOGD("发现目标进程:%s\n", buf);
        }//if
    }//while
    pclose(pfile);
}

//父进程名检测 自身进程名检测
void CheckParents() {
    // 设置buf
    char strPpidCmdline[0x100] = {0};
    snprintf(strPpidCmdline, sizeof(strPpidCmdline), "/proc/%d/cmdline", getppid());
    // 打开文件
    int file = open(strPpidCmdline, O_RDONLY);
    if (file < 0) {
        LOGD("CheckParents open错误!\n");
        return;
    }
    // 文件内容读入内存
    memset(strPpidCmdline, 0, sizeof(strPpidCmdline));
    ssize_t ret = read(file, strPpidCmdline, sizeof(strPpidCmdline));
    if (-1 == ret) {
        LOGD("CheckParents read错误!\n");
        return;
    }
    // 没找到返回0
    char *sRet = strstr(strPpidCmdline, "zygote");
    if (NULL == sRet) {
        // 执行到这里，判定为调试状态
        LOGD("父进程cmdline没有zygote子串!\n");
        return;
    }
    int i = 0;
    return;
}

//APK线程检测
void CheckTaskCount() {
    char buf[0x100] = {0};
    char *str = "/proc/%d/task";
    snprintf(buf, sizeof(buf), str, getpid());
    // 打开目录:
    DIR *pdir = opendir(buf);
    if (!pdir) {
        perror("CheckTaskCount open() fail.\n");
        return;
    }
    // 查看目录下文件个数:
    struct dirent *pde = NULL;
    int Count = 0;
    while ((pde = readdir(pdir))) {
        // 字符过滤
        if ((pde->d_name[0] <= '9') && (pde->d_name[0] >= '0')) {
            ++Count;
            LOGD("%d 线程名称:%s\n", Count, pde->d_name);
        }
    }
    LOGD("线程个数为：%d", Count);
    if (1 >= Count) {
        // 此处判定为调试状态.
        LOGD("调试状态!\n");
    }
    int i = 0;
    return;
}

//安卓系统自带调试检测函数 分析android自带调试检测函数isDebuggerConnected()在native的实现，
//尝试在native使用
// 只写了dalvik的代码，art的就不写了
typedef unsigned char wbool;

typedef wbool (*PPP)();

void NativeIsDBGConnected() {
    void *Handle = NULL;
    Handle = dlopen("/system/lib/libdvm.so", RTLD_LAZY);
    if (NULL == Handle) {
        LOGD("dlopen打开libdvm.so失败!\n");
        return;
    }
    PPP Fun = (PPP) dlsym(Handle, "_Z25dvmDbgIsDebuggerConnectedv");
    if (NULL == Fun) {
        LOGD("dlsym获取_Z25dvmDbgIsDebuggerConnectedv失败!\n");
        return;
    } else {
        wbool ret = Fun();
        if (1 == ret) {
            // 此处判定为调试模式
            LOGD("dalvikm模式，调试状态!\n");
            return;
        }
    }
    return;
}

//ptrace检测
// 单线程ptrace
void ptraceCheck() {
// ptrace如果被调试返回值为-1，如果正常运行，返回值为0
    int iRet = ptrace(PTRACE_TRACEME, 0, 0, 0);
    if (-1 == iRet) {
        LOGD("ptrace失败，进程正在被调试\n");
        return;
    } else {
        LOGD("ptrace的返回值为:%d\n", iRet);
        return;
    }
}

//断点指令检测
// 参数1：函数首地址 参数2：函数size
//typedef uint8_t u8;
//typedef uint32_t u32;
//
//void checkbkpt(u8 *addr, u32 size) {
//// 结果
//    u32 uRet = 0;
//// 断点指令
//// u8 armBkpt[4]={0xf0,0x01,0xf0,0xe7};
//// u8 thumbBkpt[2]={0x10,0xde};
//    u8 armBkpt[4] = {0};
//    armBkpt[0] = 0xf0;
//    armBkpt[1] = 0x01;
//    armBkpt[2] = 0xf0;
//    armBkpt[3] = 0xe7;
//    u8 thumbBkpt[2] = {0};
//    thumbBkpt[0] = 0x10;
//    thumbBkpt[1] = 0xde;
//    // 判断模式
//    int mode = (u32) addr % 2;
//    if (1 == mode) {
//        LOGD("checkbkpt:(thumb mode)该地址为thumb模式\n");
//        u8 *start = (u8 *) ((u32) addr - 1);
//        u8 *end = (u8 *) ((u32) start + size);
//        // 遍历对比
//        while (1) {
//            if (start >= end) {
//                uRet = 0;
//                LOGD("checkbkpt:(no find bkpt)没有发现断点.\n");
//                break;
//            }
//            if (0 == memcmp(start, thumbBkpt, 2)) {
//                uRet = 1;
//                LOGD("checkbkpt:(find it)发现断点.\n");
//                break;
//            }
//            start = start + 2;
//        }//while
//    }//if
//    else {
//        LOGD("checkbkpt:(arm mode)该地址为arm模式\n");
//        u8 *start = (u8 *) addr;
//        u8 *end = (u8 *) ((u32) start + size);
//        // 遍历对比
//        while (1) {
//            if (start >= end) {
//                uRet = 0;
//                LOGD("checkbkpt:(no find)没有发现断点.\n");
//                break;
//            }
//            if (0 == memcmp(start, armBkpt, 4)) {
//                uRet = 1;
//                LOGD("checkbkpt:(find it)发现断点.\n");
//                break;
//            }
//            start = start + 4;
//        }//while
//    }//else
//    return;
//}

//系统源码修改检测
//安卓native下最流行的反调试方案是读取进程的status或stat来检测tracepid，原理是调试状
//态下的进程tracepid不为0
bool checkSystem() {
// 建立管道
    int pipefd[2];
    if (-1 == pipe(pipefd)) {
        LOGD("pipe() error.\n");
        return false;
    }
// 创建子进程
    pid_t pid = fork();
    LOGD("father pid is: %d\n", getpid());
    LOGD("child pid is: %d\n", pid);
// for失败
    if (0 > pid) {
        LOGD("fork() error.\n");
        return false;
    }
// 子进程程序
    int childTracePid = 0;
    if (0 == pid) {
        int iRet = ptrace(PTRACE_TRACEME, 0, 0, 0);
        if (-1 == iRet) {
            LOGD("child ptrace failed.\n");
            exit(0);
        }
        LOGD("%s ptrace succeed.\n");
// 获取tracepid
        char pathbuf[0x100] = {0};
        char readbuf[100] = {0};
        sprintf(pathbuf, "/proc/%d/status", getpid());
        int fd = openat(NULL, pathbuf, O_RDONLY);
        if (-1 == fd) {
            LOGD("openat failed.\n");
        }
        read(fd, readbuf, 100);
        close(fd);
        uint8_t *start = (uint8_t *) readbuf;
        uint8_t des[100] = {0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x50, 0x69, 0x64, 0x3A, 0x09};
        int i = 100;
        bool flag = false;
        while (--i) {
            if (0 == memcmp(start, des, 10)) {
                start = start + 11;
                childTracePid = atoi((char *) start);
                flag = true;
                break;
            } else {
                start = start + 1;
                flag = false;
            }
        }//while
        if (false == flag) {
            LOGD("get tracepid failed.\n");
            return false;
        }
// 向管道写入数据
        close(pipefd[0]); // 关闭管道读端
        write(pipefd[1], (void *) &childTracePid, 4); // 向管道写端写入数据
        close(pipefd[1]); // 写完关闭管道写端
        LOGD("child succeed, Finish.\n");
        exit(0);
    } else {
// 父进程程序
        LOGD("开始等待子进程.\n");
        waitpid(pid, NULL, NULL); // 等待子进程结束
        int buf2 = 0;
        close(pipefd[1]); // 关闭写端
        read(pipefd[0], (void *) &buf2, 4); // 从读端读取数据到buf
        close(pipefd[0]); // 关闭读端
        LOGD("子进程传递的内容为:%d\n", buf2); // 输出内容
// 判断子进程ptarce后的tracepid
        if (0 == buf2) {
            LOGD("源码被修改了.\n");
        } else {
            LOGD("源码没有被修改.\n");
        }
        return true;
    }
}

void smain() {
    bool bRet = checkSystem();
    if (true == bRet)
        LOGD("check succeed.\n");
    else
        LOGD("check failed.\n");
    LOGD("main Finish pid:%d\n", getpid());
    return;
}

//利用IDA先截获信号特性的检测
//void myhandler(int sig) {
////signal(5, myhandler);
//    printf("myhandler.\n");
//    return;
//}
//
//int g_ret = 0;
//
//int main(int argc, char **argv) {
//// 设置SIGTRAP信号的处理函数为myhandler()
//    g_ret = (int) signal(SIGTRAP, myhandler);
//    if ((int) SIG_ERR == g_ret)
//        printf("signal ret value is SIG_ERR.\n");
//// 打印signal的返回值(原处理函数地址)
//    printf("signal ret value is %x\n", (unsigned char *) g_ret);
//// 主动给自己进程发送SIGTRAP信号
//    raise(SIGTRAP);
//    raise(SIGTRAP);
//    raise(SIGTRAP);
//    kill(getpid(), SIGTRAP);
//    printf("main.\n");
//    return 0;
//}

//Inotify事件监控dump
//void thread_watchDumpPagemap() {
//    LOGD("-------------------watchDump:Pagemap-------------------\n");
//    char dirName[NAME_MAX] = {0};
//    snprintf(dirName, NAME_MAX, "/proc/%d/pagemap", getpid());
//    int fd = inotify_init();
//    if (fd < 0) {
//        LOGD("inotify_init err.\n");
//        return;
//    }
//    int wd = inotify_add_watch(fd, dirName, IN_ALL_EVENTS);
//    if (wd < 0) {
//        LOGD("inotify_add_watch err.\n");
//        close(fd);
//        return;
//    }
//    const int buflen = sizeof(struct inotify_event) * 0x100;
//    char buf[buflen] = {0};
//    fd_set readfds;
//    while (1) {
//        FD_ZERO(&readfds);
//        FD_SET(fd, &readfds);
//        int iRet = select(fd + 1, &readfds, 0, 0, 0); // 此处阻塞
//        LOGD("iRet的返回值:%d\n", iRet);
//        if (-1 == iRet)
//            break;
//        if (iRet) {
//            memset(buf, 0, buflen);
//            int len = read(fd, buf, buflen);
//            int i = 0;
//            while (i < len) {
//                struct inotify_event *event = (struct inotify_event *) &buf[i];
//                LOGD("1 event mask的数值为:%d\n", event->mask);
//                if ((event->mask == IN_OPEN)) {
//// 此处判定为有true,执行崩溃.
//                    LOGD("2 有人打开pagemap,第%d次.\n\n", i);
////__asm __volatile(".int 0x8c89fa98");
//                }
//                i += sizeof(struct inotify_event) + event->len;
//            }
//            LOGD("-----3 退出小循环-----\n");
//        }
//    }
//    inotify_rm_watch(fd, wd);
//    close(fd);
//    LOGD("-----4 退出大循环,关闭监视-----\n");
//    return;
//}
//
//void smain2() {
//// 监控/proc/pid/mem
//    pthread_t ptMem, t, ptPageMap;
//    int iRet = 0;
//// 监控/proc/pid/pagemap
//    iRet = pthread_create(&ptPageMap, NULL, (PPP) thread_watchDumpPagemap, NULL);
//    if (0 != iRet) {
//        LOGD("Create,thread_watchDumpPagemap,error!\n");
//        return;
//    }
//    iRet = pthread_detach(ptPageMap);
//    if (0 != iRet) {
//        LOGD("pthread_detach,thread_watchDumpPagemap,error!\n");
//        return;
//    }
//    LOGD("-------------------smain-------------------\n");
//    LOGD("pid:%d\n", getpid());
//    return;
//}

/**
 * 加载 so 文件的时候，会触发 OnLoad
 * 检测失败，返回 -1，App 就会 Crash
 */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
    //自己附加
//    LOGD("ptrace myself...");
//    ptrace(PTRACE_TRACEME, 0, 0, 0);

    JNIEnv *env;
//    LOGI("  JNI_OnLoad  ");
    if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
//    LOGI("  start checkSignature  ");
    if (checkSignature(env) != JNI_TRUE) {
//        LOGI("  checkSignature = false ");
        // 检测不通过，返回 -1 就会使 App crash
        return -1;
    }

    //检测自己有没有被trace
//    create_thread_check_traceid();

    return JNI_VERSION_1_6;
}